/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/embedme.git
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "Tracer.h"
#include "ArgUtil.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

using namespace std;

namespace libemb{
ArgOption::ArgOption()
{
	memset((void*)m_longOptionArray,0,sizeof(struct option)*$LONG_OPT_MAX);
}

ArgOption::~ArgOption()
{
}

bool ArgOption::addOption(std::string optionName,int args)
{
	if (optionName.size()==1)
	{
		m_shortOptions.append(optionName);
		m_shortOptionPattern.append(optionName);
		switch(args){
		case 0:
			break;
		case 1:
			m_shortOptionPattern.append(":");
			break;
		default:
			m_shortOptionPattern.append("::");
			break;
		}
	}
	else if(optionName.size()>1)
	{
		if (m_longOptionNums>=$LONG_OPT_MAX)
		{
			TRACE_ERR_CLASS("long option args out of range: %d",m_longOptionNums);
			return false;
		}
		int size = MIN($LONG_OPT_LEN,optionName.size());
		memcpy(m_longOptionName[m_longOptionNums],CSTR(optionName),size);
		m_longOptionName[m_longOptionNums][size]=0;
		struct option* opt = &(m_longOptionArray[m_longOptionNums]);
		opt->name = m_longOptionName[m_longOptionNums];
		switch(args){
		case 0:
			opt->has_arg = no_argument;
			break;
		case 1:
			opt->has_arg = required_argument;
			break;
		default:
			TRACE_ERR_CLASS("long option args cannot be: %d",args);
			return false;
		}
		opt->flag = NULL;/* flag设置为NULL时,getopt_long将返回长参数对应的val值 */
		opt->val = m_longOptionNums;/* 索引值 */
		m_longOptionNums++;
	}
	return true;
}

void ArgOption::parseArgs(int argc, char* argv[])
{
	int rc ;
	for(auto i=0; i<argc; i++)
	{
		m_argsVect.emplace_back(std::string(argv[i]));
	}
	while(1)
	{
		int longIdx=0;
		rc = getopt_long(argc,argv,CSTR(m_shortOptionPattern),m_longOptionArray,&longIdx);
		if (rc<0) /* 返回值小于0表示没有获取到选项 */
		{
			break;
		}
		else if(rc=='?')
		{
			TRACE_WARN_CLASS("getopt_long error: %s",ERRSTR);
		}
		else if(rc<$LONG_OPT_MAX) /* 长选项返回的是对应的val值 */
		{
			int index = rc;
			std::string value="";
			std::string optionName = std::string(m_longOptionName[index]);
			if (optarg!=NULL)
			{
				value = std::string(optarg);
			}
			m_optionMap.emplace(std::make_pair(optionName,value));
		}
		else /* 短选项rc值为短选项字符 */
		{
			for(auto i=0; i<m_shortOptions.size(); i++)
			{
				char ch = (char)rc;
				if (m_shortOptions[i]==ch)
				{
					std::string value="";
					std::string optionName(1,ch);
					if (optarg!=NULL)
					{
						value = std::string(optarg);
					}
					m_optionMap.emplace(std::make_pair(optionName,value));
				}
			}
		}
	}
}

bool ArgOption::getValue(const std::string& optionName, std::string& value,int valueIdx)
{
	if(optionName.empty())
	{
		return false;
	}
	
	if (valueIdx<0)
	{
		auto iter = m_optionMap.find(optionName);
		if (iter!=m_optionMap.end())
		{
			value = iter->second;
			return true;
		}
		return false;
	}
	string optionStart;
	if (optionName.size()==1)
	{
		optionStart = string("-")+optionName;
	}
	else
	{
		optionStart = string("--")+optionName;
	}
	bool start = false;
	std::vector<std::string> valueVect;
	for(auto& args: m_argsVect)
	{
		if (!start)
		{
			if (args==optionStart)
			{
				start = true;
				continue;
			}
		}
		else
		{
			if(args[0]=='-')
			{
				break;
			}
			else
			{
				valueVect.emplace_back(args);
			}
		}
	}
	if (valueVect.size()>valueIdx)
	{
		value = valueVect[valueIdx];
		return true;
	}
	return false;
}

InputReader::InputReader()
{

}

InputReader::~InputReader()
{

}

InputReader& InputReader::waitInput()
{
	 std::cin >> m_inputStr;
	 return *this;
}

bool InputReader::isString(const std::string& str)
{
	if (m_inputStr==str)
	{
		return true;
	}
	return false;
}
int InputReader::size()
{
	return m_inputStr.size();
}

int InputReader::asInt()
{
	if (m_inputStr.empty())
	{
		return 0;
	}
	return atoi(CSTR(m_inputStr));
}

float InputReader::asFloat()
{
	if (m_inputStr.empty())
	{
		return 0.0;
	}
	return atof(CSTR(m_inputStr));
}

std::string InputReader::asString()
{
	return m_inputStr;
}

const char* InputReader::asCString()
{
	return CSTR(m_inputStr);
}

}
